package service

import (
	"app-box/api_error"
	"app-box/config"
	"app-box/database"
	"app-box/database/repo"
	"app-box/models"
	"app-box/models/tables"
	"app-box/tools"
	"database/sql"
	"encoding/xml"
	"strings"
	"time"

	"github.com/imroc/req"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
)

type UserService struct {
	userRepo *repo.UserRepo
}

func NewUserService() *UserService {
	var service = new(UserService)
	service.userRepo = repo.NewUserRepo(database.DefaultDB)
	return service
}

func (s *UserService) Register(userName, password string) {
	var user tables.User
	if err := s.userRepo.GetUserByUserName(userName, &user); err != nil {
		if err != gorm.ErrRecordNotFound {
			// 不是not found 错误，说明出错了
			panic(api_error.DataBaseError(err))
		}
		// 是not found错误，说明用户不存在，开始注册
		user.UserName = userName
		cryptPass, err2 := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
		if err2 != nil {
			panic(api_error.PasswordCreateError(err2))
		}
		user.Password = sql.NullString{String: string(cryptPass), Valid: true}
		if err := s.userRepo.InsertUser(&user); err != nil {
			panic(api_error.DataBaseError(err))
		}
		return
	}
	// 查找没有出错，说明已经注册过了
	panic(api_error.UserAlreadyExist())
}

func (s *UserService) Login(userName, password string, platform models.LoginPlatform) models.TokenResult {
	var user tables.User
	if err := s.userRepo.GetUserByUserName(userName, &user); err != nil {
		if err == gorm.ErrRecordNotFound {
			panic(api_error.UserNamePasswordNotMatch())
		}
		panic(api_error.DataBaseError(err))
	}
	if err := bcrypt.CompareHashAndPassword([]byte(user.Password.String), []byte(password)); err != nil {
		panic(api_error.UserNamePasswordNotMatch())
	}

	return createTokenForUser(user.ID, platform)
}

// 单点登录
func (s *UserService) CASLogin(ticket, service string, platform models.LoginPlatform) models.TokenResult {

	casURL := config.Server.CASServiceURL + "/p3/serviceValidate?service=" + service + "&ticket=" + ticket

	resp, err := req.Get(casURL)
	if err != nil {
		panic(api_error.NetworkError(err))
	}
	var casResp CasResp
	resp.ToXML(&casResp)
	if casResp.Failure != nil {
		// 单点验证出错
		panic(api_error.CASLoginFailed(casResp.Failure.Message, casResp.Failure.Code))
	}

	user := tables.User{
		NickName:       sql.NullString{String: casResp.Success.Attributes.Sn, Valid: true},
		Mail:           sql.NullString{String: casResp.Success.Attributes.Mail, Valid: true},
		EmployeeNumber: sql.NullString{String: casResp.Success.User, Valid: true},
	}

	if index := strings.Index(user.Mail.String, "@"); index != -1 {
		user.UserName = user.Mail.String[:index]
	}

	if len(user.UserName) == 0 || len(user.NickName.String) == 0 || len(user.Mail.String) == 0 || len(user.EmployeeNumber.String) == 0 {
		panic(api_error.CASLoginFailed("空的用户信息", "EmptyUserInfo"))
	}

	if err := s.userRepo.GetUserByEmployeeNumber(user.EmployeeNumber.String, &user); err != nil {
		if err == gorm.ErrRecordNotFound { // 没有此人，第一次登陆，开始注册
			if err := s.userRepo.InsertUser(&user); err != nil {
				panic(api_error.DataBaseError(err))
			}
		} else {
			panic(api_error.DataBaseError(err))
		}
	}

	return createTokenForUser(user.ID, platform)
}

func (s *UserService) RefreshToken(refreshToken models.Token) models.TokenResult {
	if err := refreshToken.Valid(); err != nil {
		panic(err)
	}
	return createTokenForUser(refreshToken.ID, refreshToken.Platform)
}

// 根据ID和平台签发Token
func createTokenForUser(id models.IDType, platform models.LoginPlatform) models.TokenResult {
	var token = models.Token{
		ExpirationAt: time.Now().Unix() + config.User.TokenExpirationDuration,
		ID:           id,
		Platform:     platform,
		UserType:     models.UserTypeUser,
		TokenType:    models.TokenTypeLogin,
	}
	var refreshToken = models.Token{
		ExpirationAt: time.Now().Unix() + config.User.TokenRefreshExpirationDuration,
		ID:           id,
		Platform:     platform,
		UserType:     models.UserTypeUser,
		TokenType:    models.TokenTypeRefresh,
	}
	tokenStr, apiErr := tools.NewTokenTool(config.Server.TokenSalt).SignToken(token)
	if apiErr != nil {
		panic(apiErr)
	}
	refreshTokenStr, apiErr := tools.NewTokenTool(config.Server.TokenSalt).SignToken(refreshToken)
	if apiErr != nil {
		panic(apiErr)
	}
	return models.TokenResult{
		Token:        tokenStr,
		RefreshToken: refreshTokenStr,
	}
}

// CasResp 存储cas返回的xml结果
type CasResp struct {
	XMLName xml.Name `xml:"http://www.yale.edu/tp/cas serviceResponse"`

	Failure *xmlAuthenticationFailure
	Success *xmlAuthenticationSuccess
}

type xmlAuthenticationFailure struct {
	XMLName xml.Name `xml:"authenticationFailure"`
	Code    string   `xml:"code,attr"`
	Message string   `xml:",innerxml"`
}

type xmlAuthenticationSuccess struct {
	XMLName    xml.Name       `xml:"authenticationSuccess"`
	User       string         `xml:"user"` // 工号
	Attributes *xmlAttributes `xml:"attributes"`
}

type xmlAttributes struct {
	XMLName xml.Name `xml:"attributes"`
	Mail    string   `xml:"mail"` // 邮箱
	Sn      string   `xml:"sn"`   // 姓名
}
